pub trait RccRegisters {
    fn modify<F>(&self, f: F)
    where
        F: FnOnce(&mut RccConfigWriter);
}

pub struct RccConfigWriter {
    source: Option<u8>,
    prescaler: Option<u8>,
}

impl RccConfigWriter {
    pub fn set_source(&mut self, source: u8) {
        self.source = Some(source);
    }

    pub fn set_prescaler(&mut self, prescaler: u8) {
        self.prescaler = Some(prescaler);
    }
}

pub unsafe fn apply_clock_settings<R: RccRegisters>(
    rcc: &R,
    source: u8,
    prescaler: Option<u8>,
) {
    rcc.modify(|w| {
        w.set_source(source);
        if let Some(p) = prescaler {
            w.set_prescaler(p);
        }
    });
}

#[cfg(test)]
mod tests {
    use super::*;

    pub struct MockRcc {
        writer: RccConfigWriter,
    }

    impl RccRegisters for MockRcc {
        fn modify<F>(&self, f: F)
        where
            F: FnOnce(&mut RccConfigWriter),
        {
            let mut writer = RccConfigWriter {
                source: None,
                prescaler: None,
            };
            f(&mut writer);

            assert!(writer.source.is_some(), "Source not set in modify");
            assert!(writer.prescaler.is_some() || writer.prescaler.is_none(), "Prescaler handling failed");
        }
    }

    #[test]
    fn test_apply_clock_settings_with_prescaler() {
        let mock_rcc = MockRcc {
            writer: RccConfigWriter {
                source: None,
                prescaler: None,
            },
        };

        unsafe {
            apply_clock_settings(&mock_rcc, 3, Some(5));
        }
    }

    #[test]
    fn test_apply_clock_settings_without_prescaler() {
        let mock_rcc = MockRcc {
            writer: RccConfigWriter {
                source: None,
                prescaler: None,
            },
        };

        unsafe {
            apply_clock_settings(&mock_rcc, 2, None);
        }
    }
}
